/*
 * Created by SharpDevelop.
 * User: David
 * Date: 2006-11-26
 * Time: 02:10
 * 
 * To change this template use Tools | Options | Coding | Edit Standard Headers.
 */

using System;
using System.Windows.Forms;

using tools;

using Labb4.Pooman.Game;

namespace Labb4.Media.Sfx
{
	/// <summary>
	/// Plays the sounds. Before using it, you have to call Initialize on it. Important to call
	/// TearDown before exiting the program.
	/// 
	/// Implements the Singleton design pattern
	/// </summary>
	public sealed class FMODManager : Manager
	{
		#region members
		
		/// <summary>
		/// The one instance
		/// </summary>
		private static FMODManager			m_Instance = new FMODManager();
		/// <summary>
		/// The engine playing the sounds
		/// </summary>
		private FMOD.System					m_FMODEngine;
		/// <summary>
		/// A property for no reverb
		/// </summary>
		private FMOD.REVERB_PROPERTIES		m_ReverbOff;
		/// <summary>
		/// A property for reverb
		/// </summary>
		private FMOD.REVERB_PROPERTIES		m_ReverbOn;
		/// <summary>
		/// The number of virtual channels playing simultaneously at the moment
		/// </summary>
		private int							m_ChannelsPlaying;
		/// <summary>
		/// At all times holds the maximum number of simultaneously playing channels "this far"
		/// </summary>
		private int							m_MaxSimChans;
		
		/// <summary>
		/// Reference to the properties class
		/// </summary>
		private Properties					m_Settings;
		/// <summary>
		/// True if the sound is muted, otherwise false
		/// </summary>
		private bool						m_Muted;
		
		#endregion
		
		#region constructors
		
		/// <summary>
		/// Constructor, initializes members
		/// </summary>
		private FMODManager() :
			base()
		{
			m_FMODEngine = null;
			
			FMOD.PRESET	Preset = new FMOD.PRESET();
			m_ReverbOff = Preset.OFF();
			m_ReverbOn = Preset.STONEROOM();
			
			m_ChannelsPlaying = 0;
			m_MaxSimChans = 0;
			
			m_Settings = Properties.Instance;
			m_Muted = false;
		}
		
		#endregion
		
		#region methods
		
		/// <summary>
		/// Creates an instance of an FMOD sound from a file
		/// </summary>
		/// <param name="fileName">The name of the file holding the audio data (wav)</param>
		/// <returns>The FMOD.Sound holding the audio data</returns>
		public FMOD.Sound GetFMODSound(string fileName)
		{
			FMOD.RESULT		result;
			FMOD.Sound		sound = null;
			
			result = m_FMODEngine.createSound(fileName, FMOD.MODE.LOOP_OFF | FMOD.MODE._2D | FMOD.MODE.SOFTWARE | FMOD.MODE.CREATESAMPLE, ref sound);
			ERRCHECK(result);
			
			return sound;
		}
		
		/// <summary>
		/// Plays a sound
		/// </summary>
		/// <param name="sound">The sound to play</param>
		/// <param name="channel">The channel to play on</param>
		/// <param name="restart">If this is true, and the sound is allready playing, it will be restarted</param>
		/// <param name="volume">The volume, ranges from 0.0 to 1.0</param>
		/// <param name="pan">The pan position, ranges from -1.0 to 1.0</param>
		public void PlaySample(FMOD.Sound sound, ref FMOD.Channel channel, bool restart, float volume, float pan)
		{
			FMOD.RESULT				result;
			FMOD.CHANNELINDEX		channelIndex = FMOD.CHANNELINDEX.FREE;
			
			if(restart)
			{
				channelIndex = FMOD.CHANNELINDEX.REUSE;
				
				if(channel != null)
				{
					result = channel.stop();
					
					if((result != FMOD.RESULT.ERR_INVALID_HANDLE) && (result != FMOD.RESULT.ERR_CHANNEL_STOLEN))
					{
						ERRCHECK(result);
					}
				}
			}
			
			if(m_Settings.MuteAudio)
			{
				volume = 0.0f;
			}
			
			result = m_FMODEngine.playSound(channelIndex, sound, true, ref channel);
			ERRCHECK(result);
			result = channel.setVolume(volume);
			ERRCHECK(result);
			result = channel.setPan(pan);
			ERRCHECK(result);
			result = channel.setPaused(false);
			ERRCHECK(result);
		}
		
		/// <summary>
		/// Turns the reverb on and off
		/// </summary>
		/// <param name="reverbOn">True to turn the reverb on, false to turn it off</param>
		public void Reverb(bool reverbOn)
		{
			FMOD.RESULT					result;
			
			if(reverbOn)
			{
				result = m_FMODEngine.setReverbProperties(ref m_ReverbOff);
			}
			else
			{
				result = m_FMODEngine.setReverbProperties(ref m_ReverbOn);
			}
			
			ERRCHECK(result);
		}
		
		/// <summary>
		/// Checks if a result to an operation is good. If not, a message is displayed
		/// explaining what went wrong.
		/// </summary>
		/// <param name="result">The result to check</param>
		public void ERRCHECK(FMOD.RESULT result)
		{
			if(result != FMOD.RESULT.OK)
			{
				MessageBox.Show("FMOD error! " + result + " - " + FMOD.Error.String(result));
				Environment.Exit(-1);
			}
		}
		
		#endregion
		
		#region overrides
		
		/// <summary>
		/// The initialization routine, fires up the FMOD audio engine
		/// </summary>
		protected override void DoInitialize()
		{
			FMOD.RESULT		result;
			uint			version = 0;
			
			result = FMOD.Factory.System_Create(ref m_FMODEngine);
			ERRCHECK(result);
			
			result = m_FMODEngine.getVersion(ref version);
			ERRCHECK(result);
			
			if(version < FMOD.VERSION.number)
			{
				MessageBox.Show("Error!  You are using an old version of FMOD " + version.ToString("X") + ".  This program requires " + FMOD.VERSION.number.ToString("X") + ".");
				Application.Exit();
			}
			
			result = m_FMODEngine.init(32, FMOD.INITFLAG.NORMAL, (IntPtr)null);
			ERRCHECK(result);
		}
		
		/// <summary>
		/// Tears down the FMOD engine
		/// </summary>
		protected override void DoTearDown()
		{
			FMOD.RESULT result;
			
			if(m_FMODEngine != null)
			{
				result = m_FMODEngine.close();
				ERRCHECK(result);
				
				result = m_FMODEngine.release();
				ERRCHECK(result);
				
				m_FMODEngine = null;
			}
		}
		
		/// <summary>
		/// Updates the FMOD engine to keep it playing in sync
		/// The instructions to FMOD says this has to be done like this
		/// </summary>
		/// <param name="deltaTime"></param>
		protected override void DoUpdate(ref int deltaTime)
		{
			if(m_FMODEngine != null)
			{
				m_FMODEngine.getChannelsPlaying(ref m_ChannelsPlaying);
				m_FMODEngine.update();
				
				if(m_ChannelsPlaying > m_MaxSimChans)
				{
					m_MaxSimChans = m_ChannelsPlaying;
				}
			}
		}
		
		#endregion
		
		#region properties
		
		/// <summary>
		/// The one instance (get)
		/// </summary>
		public static FMODManager Instance
		{
			get
			{
				return m_Instance;
			}
		}
		
		/// <summary>
		/// The number of channels currently playing (get)
		/// </summary>
		public int ChannelsCurrentlyPlaying
		{
			get
			{
				return m_ChannelsPlaying;
			}
		}
		
		/// <summary>
		/// The maximum number of channels used simultaneously this far (get)
		/// </summary>
		public int MaxChannelsUsed
		{
			get
			{
				return m_MaxSimChans;
			}
		}
		
		/// <summary>
		/// The state of the FMOD engine's mute button (get/set)
		/// </summary>
		public bool Muted
		{
			get
			{
				return m_Muted;
			}
			
			set
			{
				m_Muted = value;
			}
		}
		
		#endregion
	}
}
